/* 
 * Enterprise Library Extensions - DI + ORM.
 * Copyright (C) 2006
 *
 * GNU General Public License
 *
 * This program is free software; you can redistribute 
 * it and/or modify it under the terms of the GNU General Public License 
 * as published by the Free Software Foundation; either version 2 of the 
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, but 
 * WITHOUT ANY WARRANTY; without even the implied warranty of 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
 * See the GNU General Public License for more details.
 */
using System;
using System.Collections;
using System.Reflection;
using ELE.Common.Util;

namespace ELE.EnterpriseLibrary.Naming.Configuration
{
	/// <summary>
	/// Summary description for ContextConfiguration.
	/// </summary>
	public class ContextConfiguration : GenericConfiguration
  {
    /// <summary>
    /// The name of the tag for the environment settings
    /// </summary>
    public const string TAGNAME_ENVIRONMENT = "environment";
    /// <summary>
    /// Tag name in the ONDI config file that identifies a subcontext
    /// </summary>
    public const string TAGNAME_SUBCONTEXT = "subContext";
    /// <summary>
    /// Tag name in the ONDI config file that identifies a subcontext as being initial
    /// </summary>
    public const string TAGNAME_INITIAL = "initial";
    /// <summary>
    /// Tag name in the ONDI config file that identifies a subcontext as being initial
    /// </summary>
    public const string TAGNAME_CONFIGFILE = "configFile";
    /// <summary>
    /// Tag name in the ONDI config file that identifies the root directory of the context
    /// </summary>
    public const string TAGNAME_ROOT = "root";
    /// <summary>
    /// Tag name in the ONDI config file that identifies the logger's configuration file of the context
    /// </summary>
    public const string TAGNAME_LOGCONFIG = "logConfig";
    /// <summary>
    /// Tag name of the container of global properties
    /// </summary>
    public const string TAGNAME_GLOBALENTRIES = "globalEntries";

    private string mRootDirectory;
    private string mLoggerConfiguration;
    private Hashtable mEnvironment;
    private Hashtable mGlobalEntries;
    private Hashtable mSubContexts;
    /// <summary>
    /// Create a new one
    /// </summary>
		public ContextConfiguration()
		{
		}
    /// <summary>
    /// 
    /// </summary>
    public Hashtable Environment
    {
      get { return mEnvironment; }
      set { mEnvironment = value; }
    }
    /// <summary>
    /// 
    /// </summary>
    public Hashtable GlobalEntries
    {
      get { return mGlobalEntries; }
      set { mGlobalEntries = value; }
    }
    /// <summary>
    /// 
    /// </summary>
    public Hashtable SubContexts
    {
      get { return mSubContexts; }
      set { mSubContexts = value; }
    }
    /// <summary>
    /// The root of the config files
    /// </summary>
    [System.Xml.Serialization.XmlElementAttribute("root")] 
    public string RootDirectory
    {
      get { return mRootDirectory; }
      set { mRootDirectory = value;}
    }
    /// <summary>
    /// The root of the config files
    /// </summary>
    [System.Xml.Serialization.XmlElementAttribute("logConfig")]
    public string LoggerConfigurationFile
    {
      get { return mLoggerConfiguration; }
      set { mLoggerConfiguration = value; }
    }
    /// <summary>
    /// Resolves links to other variables
    /// </summary>
    public void ResolveReferences()
    {
      this.GlobalEntries = ResolveKeysInHashtable(this.GlobalEntries);
      this.Environment = ResolveKeysInHashtable(this.Environment);
      this.LoggerConfigurationFile = ResolveKey(null, this.LoggerConfigurationFile);
    }
    private object TranslateConfiguredObject(object obj, string stringValueType)
    {
      if (obj is IDictionary)
      {
        return ResolveKeysInHashtable((IDictionary)obj);
      }
      else if (obj is IList)
      {
        return ResolveKeysInList((IList)obj, null);
      }
      else if (obj is GenericCollectionInitializer)
      {
        return ResolveKeysInGenericCollection((GenericCollectionInitializer)obj);
      }
      else if (obj is string)
      {
        return Convert.ChangeType(obj, Type.GetType(stringValueType));
      }
      return obj;
    }
    private object ResolveKeysInGenericCollection(GenericCollectionInitializer initializer)
    {
      if (initializer.Type == 0)
      {
        //Entries are list
        object o = ObjectFactory.CreateGenericList(initializer.GenericType, initializer.GenericValueType);
        foreach (object entry in initializer.Entries)
        {
          object val = TranslateConfiguredObject(entry, initializer.GenericValueType);
          MethodInfo method = o.GetType().GetMethod("Add");
          object[] pars = { val };
          method.Invoke(o, pars);
        }
        return o;
      }
      else if (initializer.Type == 1)
      {
        //Entries are dictionaries
        object o = ObjectFactory.CreateGenericDictionary(initializer.GenericType, initializer.GenericKeyType, initializer.GenericValueType);
        foreach (KeyValuePair entry in initializer.Entries)
        {
          object key = TranslateConfiguredObject(entry.Key, initializer.GenericKeyType);
          object val = TranslateConfiguredObject(entry.Value, initializer.GenericValueType);
          MethodInfo method = o.GetType().GetMethod("Add");
          object[] pars = { key, val };
          method.Invoke(o, pars);
        }
        return o;
      }
      return initializer;
    }
    private Hashtable ResolveKeysInHashtable(IDictionary source)
    {
      if(source != null)
      {
        Hashtable clone = new Hashtable();
        foreach(string key in source.Keys)
        {
          if(source[key] is IDictionary)
          {
            clone.Add(key, ResolveKeysInHashtable((IDictionary)source[key]));
          }
          else if(source[key] is IList)
          {
            clone.Add (key, ResolveKeysInList((IList)source[key], key)); 
          }
          else if (source[key] is GenericCollectionInitializer)
          {
            clone.Add(key, ResolveKeysInGenericCollection((GenericCollectionInitializer)source[key]));
          }
          else if (source[key] is string)
          {
            clone.Add(key, ResolveKey(key, source[key].ToString()));
          }
          else
          {
            clone.Add(key, source[key]);
          }
        }
        return clone;
      }
      return null;
    }
    private IList ResolveKeysInList(IList source, string key)
    {
      if(source != null)
      {
        ArrayList clone = new ArrayList();
        foreach(object item in source)
        {
          if(item is IDictionary)
          {
            clone.Add(ResolveKeysInHashtable((IDictionary)item));
          }
          else if(item is IList)
          {
            clone.Add(ResolveKeysInList((IList)item, key));
          }
          else if (item is GenericCollectionInitializer)
          {
            clone.Add(ResolveKeysInGenericCollection((GenericCollectionInitializer)item));
          }
          else if(item is string)
          {
            clone.Add( ResolveKey(key,item.ToString()));
          }
          else
          {
            clone.Add(item);
          }
        }
        return clone;
      }
      return null;
    }
    private string ResolveKey(string key, string value)
    {
      string rootRef = ESCAPE_LINK_START + "root" + ESCAPE_LINK_END;
      while (StringUtils.Contains(value, ESCAPE_LINK_START))
      {
          
        if (StringUtils.ContainsIgnoreCase(value, rootRef))
        {
          value = StringUtils.ReplaceIgnoreCase(value, rootRef, this.RootDirectory);
        }
        else
        {
          string keyToLook = StringUtils.ExtractFromDelimiters(value, ESCAPE_LINK_START, ESCAPE_LINK_END);
          if(key != null && keyToLook.ToLower().Equals(key.ToLower()))
          {
            value = StringUtils.ReplaceIgnoreCase(value, ESCAPE_LINK_START + keyToLook + ESCAPE_LINK_END, "");
          }
          else
          {
            string resp = FindValueForKeyInObject(this.GlobalEntries, keyToLook);
            if(resp != null)
            {
              value = StringUtils.ReplaceIgnoreCase(value, ESCAPE_LINK_START + keyToLook + ESCAPE_LINK_END, resp);
            }
            else
            {
              value = StringUtils.ReplaceIgnoreCase(value, ESCAPE_LINK_START + keyToLook + ESCAPE_LINK_END, "");
            }
          }
        }
      }
      return value;
    }
    private string FindValueForKeyInObject(object source, string keyName)
    {
      if(source is IDictionary)
      {
        return FindValueForKeyInHastable((IDictionary)source, keyName);
      }
      else if(source is IList)
      {
        return FindValueForKeyInList((IList)source, keyName);
      }
      else if(source is string)
      {
        return (string)source;
      }
      return null;
    }
    private string FindValueForKeyInHastable(IDictionary source, string keyName)
    {
      string currentKey = GetCurrentKey(keyName);
      string subKey = GetSubKey(keyName);

      foreach(object key in source.Keys)
      {
        if(key.ToString().ToLower().Equals(currentKey.ToLower()))
        {
          object obj = source[key];
          if(subKey != null && subKey.Length > 0 && !(source[key] is string))
          {
            return FindValueForKeyInObject(source[key],subKey);
          }
          else
          {
            return source[key].ToString();
          }
        }    
      }
      return null;
    }
    private string FindValueForKeyInList(IList source, string keyName)
    {
      string currentKey = GetCurrentKey(keyName);
      string subKey = GetSubKey(keyName);
      if(currentKey != null)
      {
        try
        {
          int indx = Convert.ToInt32(currentKey);
          object obj = source[indx];
          if(obj != null)
          {
            if(!(obj is string) && subKey.Length > 0)
            {
               return FindValueForKeyInObject(obj, subKey);
            }
            else
            {
              return obj.ToString();
            }
          }
          else
          {
            return null;
          }
        }
        catch
        {
          return null;
        }
      }
      else
      {
        return source.ToString();
      }
    }

    
    private string GetSubKey(string keyName)
    {
      string subKey = "";
      if(StringUtils.Contains( keyName,ESCAPE_INDEX_START) && StringUtils.Contains(keyName, ESCAPE_INDEX_END) )
      {
        subKey = StringUtils.ExtractFromDelimiters(keyName, ESCAPE_INDEX_START, ESCAPE_INDEX_END);

        string restKey = keyName.Substring(keyName.IndexOf(ESCAPE_INDEX_START) + 1);
        if(StringUtils.Contains(restKey, ESCAPE_INDEX_START))
        {
          subKey += restKey.Substring(restKey.IndexOf(ESCAPE_INDEX_START));
        }
      }
      return subKey;
    }
    private string GetCurrentKey(string keyName)
    {
      string currentKey = keyName;
      if(StringUtils.Contains( keyName,ESCAPE_INDEX_START) && StringUtils.Contains(keyName, ESCAPE_INDEX_END) )
      {
        currentKey = keyName.Substring(0, keyName.IndexOf(ESCAPE_INDEX_START));
      }
      return currentKey;
    }

  }
  /// <summary>
  /// 
  /// </summary>
  public class EnvironmentClassName
  {
    private string mClass;
    private string mAssembly;
    /// <summary>
    /// The class name
    /// </summary>
    [System.Xml.Serialization.XmlElementAttribute("class")] 
    public string ClassName
    {
      get{return mClass;}
      set{mClass = value;}
    }
    /// <summary>
    /// The assemblyname
    /// </summary>
    [System.Xml.Serialization.XmlElementAttribute("assembly")] 
    public string AssemblyName
    {
      get
      {
        if(mAssembly == null || mAssembly.Equals(String.Empty))
        {
          return this.GetType().Assembly.FullName;
        }
        return mAssembly;
      }
      set
      {
        mAssembly = value;
      }
    }
  }
}
